﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;

namespace CodeDOM3 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();

        }
        

        private void button1_Click(object sender, EventArgs e) {

            if (!string.IsNullOrEmpty(comboBox1.Text)) {

                CodeDomProvider provider = null;
                switch (comboBox1.Text) {
                    case "C#":
                        provider = CodeDomProvider.CreateProvider("CSharp");
                        break;
                    case "VB":
                        provider = CodeDomProvider.CreateProvider("vb");
                        break;
                    case "C++":
                        provider = CodeDomProvider.CreateProvider("cpp");
                        break;
                }
                var result = Generate();
                CodeGeneratorOptions opt = new CodeGeneratorOptions();
                using (System.IO.StringWriter writer = new System.IO.StringWriter()) {
                    provider.GenerateCodeFromCompileUnit(result, writer, opt);
                    textBox1.Text = writer.ToString();
                }
            } else {
                MessageBox.Show("Please select language first.");
            }
        }


        private static CodeCompileUnit Generate() {
            CodeCompileUnit target = new CodeCompileUnit();
            CodeAttributeDeclaration assemblyAttr =
                new CodeAttributeDeclaration(
                    "System.Reflection.AssemblyVersion",
                    new CodeAttributeArgument(new CodePrimitiveExpression("0.9.0.0"))
                    );
            target.AssemblyCustomAttributes.Add(assemblyAttr);

            CodeNamespace ns = new CodeNamespace("IPE.IntegratedSystem.ObjectModel");
            ns.Imports.Add(new CodeNamespaceImport("System"));

            CodeTypeDeclaration myClass = new CodeTypeDeclaration("Process");
            myClass.IsClass = true;
            myClass.TypeAttributes = TypeAttributes.Public;
            myClass.BaseTypes.Add(new CodeTypeReference(typeof(System.ComponentModel.INotifyPropertyChanged)));

            CodeSnippetTypeMember exp = new CodeSnippetTypeMember("public virtual event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
            myClass.Members.Add(exp);

            myClass.AddPropertyWithEvent("int64", "ID");
            myClass.AddPropertyWithEvent("string", "ProcessName");

            ns.Types.Add(myClass);
            target.Namespaces.Add(ns);

            return target;
        }

        

        private void button2_Click(object sender, EventArgs e) {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "*.dll|*.dll";
            DialogResult result = dialog.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK) {
                if (!string.IsNullOrEmpty(dialog.FileName)) {
                    var unit = Generate();
                    CSharpCodeProvider provider = new CSharpCodeProvider();

                    System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
                    parameters.GenerateExecutable = false;
                    parameters.OutputAssembly = dialog.FileName;
                    parameters.ReferencedAssemblies.Add("System.dll");
                    parameters.TreatWarningsAsErrors = false;
                    parameters.IncludeDebugInformation = true;

                    CompilerResults compilerResult = provider.CompileAssemblyFromDom(parameters, unit);
                    if (compilerResult.Errors.Count == 0) {
                        MessageBox.Show("Success");
                    } else {
                        MessageBox.Show("Failed");
                    }
                }
            }
        }
    }

    public static class CodeTypeDeclarationExtension {
        public static void AddPropertyWithEvent(this CodeTypeDeclaration targetClass, string propertyTypeName, string propertyName) {
            string fieldName = "_" + propertyName;
            
            Type propertyType = Type.GetType("System."+propertyTypeName,true,true);
            CodeMemberField idField = new CodeMemberField(new CodeTypeReference(propertyType), fieldName);
            CodeMemberProperty idProperty = new CodeMemberProperty();
            idProperty.Attributes = MemberAttributes.Public; //默认是 virtual
            idProperty.Name = propertyName;
            idProperty.Type = new CodeTypeReference(propertyType);
            idProperty.HasGet = true;
            idProperty.HasSet = true;
            idProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), fieldName)));
            idProperty.SetStatements.Add(new CodeAssignStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), fieldName),
                new CodePropertySetValueReferenceExpression()));
            CodeObjectCreateExpression createArgs = new CodeObjectCreateExpression(
                new CodeTypeReference(typeof(PropertyChangedEventArgs)),
                new CodePrimitiveExpression(propertyName));
            CodeDelegateInvokeExpression raiseEvent = new CodeDelegateInvokeExpression(
                new CodeVariableReferenceExpression("PropertyChanged"),
                new CodeThisReferenceExpression(),
                createArgs);

            CodeConditionStatement ifstatement = new CodeConditionStatement(
                new CodeSnippetExpression("PropertyChanged != null"),
                new CodeExpressionStatement(raiseEvent)
                );

            idProperty.SetStatements.Add(ifstatement);
            targetClass.Members.Add(idField);
            targetClass.Members.Add(idProperty);
        }
    }
}


